package com.btcode.reflect;

import com.btcode.exception.MsgException;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

public class FileClassLoader implements IClassLoader {

    private IClassFinder classFinder;

    public FileClassLoader(IClassFinder classFinder) {
        this.classFinder = classFinder;
    }

    public List<Class<?>> loadClass(Class<?> myClass, String packetName)
        throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {

        try {
            List<String> classPathList = getClassPathList(myClass, packetName);
            List<String> classNameList = getClassNameList(myClass, classPathList);
            List<Class<?>> classList = new ArrayList<>();

            for (String className : classNameList) {

                try {

                    if (className == null || className.trim().equals("")) {
                        continue;
                    }

                    Class<?> loadClass = myClass.getClassLoader().loadClass(className);

                    if (classFinder.isMatch(loadClass)) {
                        classList.add(loadClass);
                    }

                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }

            return classList;
        }
        catch (Exception e) {
            throw new MsgException("加载类出错", e);
        }

    }

    /**
     * 把类路径集合，转换成类名
     */
    private List<String> getClassNameList(Class<?> myClass, List<String> classPathList)
        throws IOException {

        List<String> classNameList = new ArrayList<>();
        String contextBasePath = getContextBasePath(myClass);

        for (String file : classPathList) {

            String classPath = file;

            /**
             * 如果是以file开头，说明是URL，需要根据所提供的myClass，分析出代码根目录，再把URL信息剔除掉代码根目录信息，得到类信息
             */
            if (file.startsWith("file")) {
                classPath = file.replaceFirst(contextBasePath, "");
            }

            classNameList.add(classPath.replaceAll("/", ".").replaceFirst("^\\.", "")
                                  .replace(".class", ""));
        }

        return classNameList;
    }

    private String getContextBasePath(Class<?> myClass) throws IOException {

        String className = myClass.getName();
        String packName = myClass.getPackage().getName();
        className = className.substring(className.lastIndexOf(".") + 1, className.length())
            + ".class";

        String classSubPath = packName.replaceAll("\\.", "/") + "/" + className;

        String resourcePath = myClass.getResource(className).toString().replace(classSubPath, "");

        return resourcePath;
    }

    /**
     * 根据指定的class，获取对应包下的所有类文件
     */
    private List<String> getClassPathList(Class<?> myClass, String packetName)
        throws IOException, URISyntaxException {
        packetName = packetName.replaceAll("\\.", "/") + "/";

        Enumeration<URL> dirs = myClass.getClassLoader().getResources(packetName);

        String basePath = getContextBasePath(myClass);
        List<String> classPathList = new ArrayList<>();
        String path = "";

        while (dirs.hasMoreElements()) {
            URL url = dirs.nextElement();
            String uriString = url.toURI().toString();
            path = url.getPath();
            /**
             * 只扫描myClass上下文相关的资源
             */
            if (uriString.startsWith(basePath)) {
                classPathList.addAll(getFilePathList(new File(path), basePath));
            }
        }

        return classPathList;
    }

    private List<String> getFilePathList(File file, String baseRemovePath) {
        List<String> result = new ArrayList<>();
        if (!file.isDirectory()) {
            if (file.getName().startsWith("_")) {
                return result;
            }
            String filePath = replaceBasePath(file.toURI().toString(), baseRemovePath);
            result.add(filePath);
        }
        else {
            File[] files = file.listFiles();
            if(files != null){
                for (File fileItem : files) {
                    result.addAll(getFilePathList(fileItem, baseRemovePath));
                }
            }

        }
        return result;
    }

    private String replaceBasePath(String targetPath, String replacePath) {
        if (replacePath != null) {
            targetPath = targetPath.replaceAll("\\\\", "/");
            replacePath = replacePath.replaceAll("\\\\", "/");
            String result = targetPath.replaceAll(replacePath, "");
            return result;
        }
        return targetPath;
    }

}
